Part 2 : Stream Reasoning with Jasper

In this part of the tutorial, we will see how to query RDF Streams under an simple entailment regime using a stream reasoner called Jasper.

The JASPER is an RSP engine that offers rule-based reasoning capabilities. It consumes queries written in RSP-QL syntax and fully implements the RSP-QL reference model.

RDFLib

RSPLib is the python library that we are going to use in our tutorial. It offers abstrations to manipulate RSPSources, e.g. RDF Streams and to interact with RSP Engines, e.g. registering streams and queries.

RSPLib APIs are still under development. In this tutorial we are going to use version 0.3.4 which is available at https://pypi.python.org/pypi/rsplib/.

to upgrade it just type !pip instal rsplib --upgrade in a cell

source code available at https://github.com/streamreasoning/rsplib


In [37]:
from IPython.display import Image
from IPython.core.display import HTML 
from rsplib.processing import RSPSource, StreamReasoner

Now one we assume that RDF Stream are up and running from Part 1. If you did not followed part one please follow the link below and complete the part about TripleWave and RDF Streams.

http://localhost:8888/notebooks/work/streamapp/StreamApp%20-%20Part%201%20-%20%20RDF%20Stream%20Processing%20with%20the%20CSPARQL%20engine.ipynb

Stream Reasoner: Jasper

Now that we have our RDF Stream running, we can start thinking about how to query them. To this extent, we are going to use jasper.

Did you started csparql in the consumer folder?

RSPLib offers a facade to communicate with Stream Reasoners via the SR Services (a RESTful interface for SRs).


In [21]:
jasper = StreamReasoner("http://jasper", 8183);
jasper.status()


{"name":"jasper","host":"jasper/","runUUID":"88e4f7ee-c18d-11e7-b712-1a00a83a5601","port":8183,"empty_results":false,"inference":false,"timestam_function":false,"backloop":false,"num_streams":4,"num_datasets":0,"num_queries":0}
Out[21]:
{'backloop': False,
 'empty_results': False,
 'host': 'jasper/',
 'inference': False,
 'name': 'jasper',
 'num_datasets': 0,
 'num_queries': 0,
 'num_streams': 4,
 'port': 8183,
 'runUUID': '88e4f7ee-c18d-11e7-b712-1a00a83a5601',
 'timestam_function': False}

Registering Streams


In [18]:
jasper.register_stream("AarhusTrafficData158505", "http://aarhustrafficdata158505:4001/sgraph")


"Stream AarhusTrafficData158505 succesfully registered with IRI http://aarhustrafficdata158505:4001/sgraph"
Out[18]:
'Stream AarhusTrafficData158505 succesfully registered with IRI http://aarhustrafficdata158505:4001/sgraph'

In [19]:
jasper.register_stream("AarhusTrafficData182955", "http://aarhustrafficdata182955:4000/sgraph")


"Stream AarhusTrafficData182955 succesfully registered with IRI http://aarhustrafficdata182955:4000/sgraph"
Out[19]:
'Stream AarhusTrafficData182955 succesfully registered with IRI http://aarhustrafficdata182955:4000/sgraph'

Adding domain knowledge: TBOX

In order to perform some reasoning, we require to capture some knowledge about our domain.

In this example the ontology available at here

contains a simple hierarchi

CongestionLevelA and CongenstionLevelB are subclasses of CongestionLevelT.


In [41]:
Image(url= "https://raw.githubusercontent.com/streamreasoning/rsplab/tutorial/collector/lab/streamapp/images/tbox.png")


Out[41]:

If we have a look to our streams again:

we will observer that either CongestionLevelA or CongenstionLevelB are present.

We are now going to show how to look for CongestionLevelT adding some reasoning.


In [40]:
tbox = "https://raw.githubusercontent.com/streamreasoning/rsplab/tutorial/collector/lab/streamapp/tbox.rdf"

Load Some rules from our file, e.g. RDFS entailment


In [15]:
with open('rdfs.rules.txt', 'r') as rule_file:
    rdfs=rule_file.read()
rdfs


Out[15]:
'[rdf1and4: (?x ?p ?y) -> (?p rdf:type rdf:Property), (?x rdf:type rdfs:Resource), (?y rdf:type rdfs:Resource)]\n[rdfs7b: (?a rdf:type rdfs:Class) -> (?a rdfs:subClassOf rdfs:Resource)] \n\n[rdfs2:  (?x ?p ?y), (?p rdfs:domain ?c) -> (?x rdf:type ?c)] \n[rdfs3:  (?x ?p ?y), (?p rdfs:range ?c) -> (?y rdf:type ?c)] \n[rdfs5a: (?a rdfs:subPropertyOf ?b), (?b rdfs:subPropertyOf ?c) -> (?a rdfs:subPropertyOf ?c)] \n[rdfs5b: (?a rdf:type rdf:Property) -> (?a rdfs:subPropertyOf ?a)] \n[rdfs6:  (?a ?p ?b), (?p rdfs:subPropertyOf ?q) -> (?a ?q ?b)] \n[rdfs7:  (?a rdf:type rdfs:Class) -> (?a rdfs:subClassOf ?a)]\n[rdfs8:  (?a rdfs:subClassOf ?b), (?b rdfs:subClassOf ?c) -> (?a rdfs:subClassOf ?c)] \n[rdfs9:  (?x rdfs:subClassOf ?y), (?a rdf:type ?x) -> (?a rdf:type ?y)] \n[rdfs10: (?x rdf:type rdfs:ContainerMembershipProperty) -> (?x rdfs:subPropertyOf rdfs:member)] \n'

Register RuleSet


In [16]:
jasper.register_rules("rdfs", rdfs)


{'ruleset': 'rdfs', 'rules': '[rdf1and4: (?x ?p ?y) -> (?p rdf:type rdf:Property), (?x rdf:type rdfs:Resource), (?y rdf:type rdfs:Resource)]\n[rdfs7b: (?a rdf:type rdfs:Class) -> (?a rdfs:subClassOf rdfs:Resource)] \n\n[rdfs2:  (?x ?p ?y), (?p rdfs:domain ?c) -> (?x rdf:type ?c)] \n[rdfs3:  (?x ?p ?y), (?p rdfs:range ?c) -> (?y rdf:type ?c)] \n[rdfs5a: (?a rdfs:subPropertyOf ?b), (?b rdfs:subPropertyOf ?c) -> (?a rdfs:subPropertyOf ?c)] \n[rdfs5b: (?a rdf:type rdf:Property) -> (?a rdfs:subPropertyOf ?a)] \n[rdfs6:  (?a ?p ?b), (?p rdfs:subPropertyOf ?q) -> (?a ?q ?b)] \n[rdfs7:  (?a rdf:type rdfs:Class) -> (?a rdfs:subClassOf ?a)]\n[rdfs8:  (?a rdfs:subClassOf ?b), (?b rdfs:subClassOf ?c) -> (?a rdfs:subClassOf ?c)] \n[rdfs9:  (?x rdfs:subClassOf ?y), (?a rdf:type ?x) -> (?a rdf:type ?y)] \n[rdfs10: (?x rdf:type rdfs:ContainerMembershipProperty) -> (?x rdfs:subPropertyOf rdfs:member)] \n'}
"Rules rdfs sucessfully registered"
Out[16]:
'Rules rdfs sucessfully registered'

In [17]:
jasper.rules()


[{"id":"rdfs","status":"STATIC"}]
Out[17]:
[{'id': 'rdfs', 'status': 'STATIC'}]

Query RDF Streams Under Graph Level Entailment

RSP-QL Syntax

the syntax we are now using is called RSP-QL syntax. The rsp w3c working group is currently working on its specification. Jasper is an early adopter for this syntax.


In [20]:
with open('q4.rspql.txt', 'r') as rspql_query:
   body = rspql_query.read()
   print(body)


PREFIX ct: <http://www.insight-centre.org/citytraffic#>
CONSTRUCT {?s a ct:CongestionLevelT }
FROM NAMED WINDOW <win1> [RANGE 30s , SLIDE 5s] ON STREAM <AarhusTrafficData158505>
FROM NAMED WINDOW <win2> [RANGE 30s , SLIDE 5s] ON STREAM <AarhusTrafficData182955>
WHERE  { WINDOW ?w { ?s a ct:CongestionLevelT }}

Registering the Query

Similarly to what we did for the CSPARQL engine, also for jasper it is necessary to register the query. However, this time we have to specify which rule set and which tbox use during the query answering.


In [25]:
jasper.register_query("ct", "STREAM", body, "rdfs", tbox)


{'queryBody': 'REGISTER STREAM <ct> AS PREFIX ct: <http://www.insight-centre.org/citytraffic#>\nCONSTRUCT {?s a ct:CongestionLevelT }\nFROM NAMED WINDOW <win1> [RANGE 30s , SLIDE 5s] ON STREAM <AarhusTrafficData158505>\nFROM NAMED WINDOW <win2> [RANGE 30s , SLIDE 5s] ON STREAM <AarhusTrafficData182955>\nWHERE  { WINDOW ?w { ?s a ct:CongestionLevelT }}', 'tbox': 'https://raw.githubusercontent.com/streamreasoning/rsplab/tutorial/collector/lab/streamapp/tbox.rdf', 'ruleset': 'rdfs'}
"Query ct succesfully registered"
Out[25]:
'Query ct succesfully registered'

Registering the Observer

Also in this case, we want to observe the results of our query answering on the web.


In [29]:
jasper.register_observer("ct", "default", {"host":"jasper","type":"ws","port":8283,"name":"default"})


"http://jasper:8183/queries/ct/observers/default"
Out[29]:
'http://jasper:8183/queries/ct/observers/default'

Measuring the Performance

RSPLab offers assisted realt-time performance monitoring using cAdvisor and Grafana. In order to observe the current status of the engine you can visit

http://localhost:3000/dashboard/db/jasper?orgId=1

in case you didn't access yet, username:admin password:admin


In [42]:
Image(url= "https://raw.githubusercontent.com/streamreasoning/rsplab/tutorial/collector/lab/streamapp/images/csparq_grafana.png")


Out[42]:

Clean Up


In [30]:
jasper.unregister_observer("ct", "default")


"Observer default (ws://jasper:8283/ct) succesfully unregistered"
Out[30]:
'Observer default (ws://jasper:8283/ct) succesfully unregistered'

In [31]:
jasper.unregister_query("ct")


"Query ct and stream jasper/queries/ct succesfully unregistered"
Out[31]:
'Query ct and stream jasper/queries/ct succesfully unregistered'

In [33]:
jasper.unregister_stream("AarhusTrafficData182955")


"Stream AarhusTrafficData182955 succesfully unregistered"
Out[33]:
'Stream AarhusTrafficData182955 succesfully unregistered'

In [34]:
jasper.unregister_stream("AarhusTrafficData158505")


"Stream AarhusTrafficData158505 succesfully unregistered"
Out[34]:
'Stream AarhusTrafficData158505 succesfully unregistered'

In [35]:
jasper.status()


{"name":"jasper","host":"jasper/","runUUID":"88e4f7ee-c18d-11e7-b712-1a00a83a5601","port":8183,"empty_results":false,"inference":false,"timestam_function":false,"backloop":false,"num_streams":2,"num_datasets":0,"num_queries":0}
Out[35]:
{'backloop': False,
 'empty_results': False,
 'host': 'jasper/',
 'inference': False,
 'name': 'jasper',
 'num_datasets': 0,
 'num_queries': 0,
 'num_streams': 2,
 'port': 8183,
 'runUUID': '88e4f7ee-c18d-11e7-b712-1a00a83a5601',
 'timestam_function': False}

To turn off jasper:

  • return to the folder rsplab/consumer
  • ./stop.sh

In [ ]: